/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "Population.h"
#include <algorithm>
#include <fstream>

Evolutive::CPopulation::CPopulation(void): m_PopSize(0),m_Population(NULL),m_ElitistInd(NULL),m_NumElitist(0),m_PopScores(NULL),m_DiversityCriteria(DIV_NONE),m_PopDiversity(NULL),m_PopFitness(NULL)
{
}

Evolutive::CPopulation::~CPopulation(void)
{
	// Remove the scores vector
	if(m_PopScores)
		delete m_PopScores;

	// Remove the diversity vector
	if(m_PopDiversity)
		delete m_PopDiversity;

	// Remove the fitness vector
	if(m_PopFitness)
		delete m_PopFitness;

	// Remove the elitist storage memory
	if(m_ElitistInd)
		delete[] m_ElitistInd;
	// Remove the population storage memory
	if(m_Population)
		delete[] m_Population;
}

void Evolutive::CPopulation::Initialize(int PopSize,int NumBits)
{
	Initialize(PopSize,1,&NumBits,CODE_GRAY);
}

void Evolutive::CPopulation::Initialize(int PopSize,int NumBits,CODING_METHOD CodMethod)
{
	Initialize(PopSize,1,&NumBits,CodMethod);
}

void Evolutive::CPopulation::Initialize(int PopSize,int NumFields,int *BitsPerField)
{
	Initialize(PopSize,NumFields,BitsPerField,CODE_GRAY);
}

void Evolutive::CPopulation::Initialize(int PopSize,int NumFields,int *BitsPerField,CODING_METHOD CodMethod)
{
	register int i;	

	// Allocate the new population
	AllocIndividuals(PopSize);
//#pragma omp parallel default(none) private(i,BitsPerField,PopSize,NumFields,CodMethod)
{
	// Initialize each individual
	for(i=0;i<PopSize;i++)
	{
		m_Population[i].SetFields(NumFields,BitsPerField);
		m_Population[i].SetCode(CodMethod);
		m_Population[i].Generate();
	}
} //END OMP
}

void Evolutive::CPopulation::AllocIndividuals(int NumIndividuals)
{
	// Store the number of individuals
	m_PopSize=NumIndividuals;

	// Remove the old population
	if(m_Population)
		delete[] m_Population;

	// Allocate the new population
	m_Population=new CChromosome[m_PopSize];
}

void Evolutive::CPopulation::SetDiversityCriteria(Evolutive::DIVERSITY_CRITERIA Criteria)
{
	m_DiversityCriteria=Criteria;
}

void Evolutive::CPopulation::SelectElitistMembers(void)
{
	register int j,k;
	CScoreStruct S;
	bool Different;
	int NumAdded;
	
	// Changes the last elements of the population with the elitist members	
	if(m_NumElitist>0)
	{
		// Add the best individual
		S=GetSortedScore(0);
		m_ElitistInd[0]=m_Population[S.Idx];

		// Add the rest of the elitist individuals (all different)
		NumAdded=1;
		for(j=0;j<m_PopSize;j++)
		{
			// If we have all the elitist members exit
			if(NumAdded==m_NumElitist)
				break;
			
			// Get the next
			S=GetSortedScore(j);
			m_ElitistInd[NumAdded]=m_Population[S.Idx];

			// Compare the last individual with the previous selected
			Different=true;
			for(k=0;k<NumAdded;k++)
			{
				if(m_ElitistInd[k]==m_ElitistInd[NumAdded])
				{
					Different=false;
					break;
				}
			}

			// If this individual is different, increment the counter
			if(Different)
				NumAdded++;
		}

		// If there are not enough different individuals do...
		if(NumAdded<m_NumElitist)
		{
			// TODO: Check the code to allows the selection of elitist individuals in strange cases
		//	throw CEvolutiveLibException("TODO: Not enought individuals",__FILE__,__LINE__,"SelectElitistMembers");
		}
	}
}

void Evolutive::CPopulation::AddElitistMembers(void)
{
	register int i;
	int Pos;
	
	// Changes the last elements of the population with the elitist members
	if(m_NumElitist>0)
	{
		Pos=m_PopSize-m_NumElitist;
		for(i=0;i<m_NumElitist;i++)
		{
			m_Population[Pos+i]=m_ElitistInd[i];
		}
	}
}

void Evolutive::CPopulation::SetNumElitist(int NumIndividuals)
{
	// Sets the new number
	m_NumElitist=NumIndividuals;

	// Creates the storage memory	
	if(m_ElitistInd)
		delete[] m_ElitistInd;
	m_ElitistInd=new CChromosome[m_NumElitist];
}

void Evolutive::CPopulation::ClearScores(void)
{
	// Remove old data	
	if(m_PopScores)
	{
		m_PopScores->clear();
		delete m_PopScores;
	}

	// Create a new vector
	m_PopScores=new CScoresVector();
}

void Evolutive::CPopulation::AddScore(unsigned int Idx,double Value)
{
	CScoreStruct S;

	// Store the data into the struct
	S.Idx=Idx;
	S.Value=Value;

	// Save the struct to the population scores
	m_PopScores->push_back(S);
}

void Evolutive::CPopulation::SortPopulationScores(Evolutive::SORT_ORDER Order)
{
	switch(Order)
	{
	case Evolutive::SORT_ASC:
		sort(m_PopScores->begin(),m_PopScores->end(),AscComp);
		break;
	case Evolutive::SORT_DESC:
		sort(m_PopScores->begin(),m_PopScores->end(),DescComp);
		break;
	}	
}

Evolutive::CChromosome& Evolutive::CPopulation::operator[] (unsigned int Idx)
{
	if(Idx<0 || Idx>=m_PopSize)
	{
		throw CEvolutiveLibException("Index out of bounds",__FILE__,__LINE__,"operator[]");
	}
	
	return m_Population[Idx];				
}

double Evolutive::CPopulation::GetSortedScoreVal (unsigned int Idx)
{
	CScoreStruct Score;

	if(Idx<0 || Idx>=m_PopSize)
	{
		throw CEvolutiveLibException("Index out of bounds",__FILE__,__LINE__,"GetSortedScoreVal");
	}

	// Obtain the element from the sorted vector
	Score=GetSortedScore(Idx);

	return Score.Value;				
}

Evolutive::CChromosome* Evolutive::CPopulation::GetSortedChromosomePtr (unsigned int Idx)
{
	CScoreStruct Score;

	if(Idx<0 || Idx>=m_PopSize)
	{
		throw CEvolutiveLibException("Index out of bounds",__FILE__,__LINE__,"GetSortedChromosomePtr");
	}

	// Obtain the element from the sorted vector
	Score=GetSortedScore(Idx);

	return &m_Population[Score.Idx];				
}

Evolutive::CChromosome* Evolutive::CPopulation::GetChromosomePtr (unsigned int Idx)
{
	if(Idx<0 || Idx>=m_PopSize)
	{
		throw CEvolutiveLibException("Index out of bounds",__FILE__,__LINE__,"GetChromosomePtr");
	}
	
	return &m_Population[Idx];				
}

int Evolutive::CPopulation::GetPopulationSize(void)
{
	return m_PopSize;
}

int Evolutive::CPopulation::GetPopulationUpdSize(void)
{
	return m_PopSize-m_NumElitist;
}

Evolutive::CScoreStruct& Evolutive::CPopulation::GetSortedScore(unsigned int Idx)
{
	return (*m_PopScores)[Idx];
}

void Evolutive::CPopulation::LoadFromFile(string FileName,int NumPars,int *NumBits,CODING_METHOD CodMethod)
{
	fstream ifs;	
	int PopSize;
	char Buffer[1024];
	char *pBuffer;
	unsigned int *Data=NULL;
	int i,NumInd;

	// Obtain the number of individuals
	PopSize=GetNumFilesFromFile(FileName);

	// Allocate the population
	AllocIndividuals(PopSize);
		
	// Open the input file
	ifs.open(FileName.data(),ios_base::in);
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"LoadFromFile");

	// Create the vector to store the paramters
	Data=new unsigned int[NumPars];

	// Read the parameters for each individual
	NumInd=0;
	while(!ifs.eof())
	{
		// Read the line
		ifs.getline(Buffer,1024);		

		// If we have an empty line continue
		if(!ifs.gcount())
			continue;		

		// Decode the lines
		pBuffer=Buffer;
		for(i=0;i<NumPars;i++)
		{
			// Verify the data
			if(!pBuffer)
				throw CEvolutiveLibException("Input file format error",__FILE__,__LINE__,"LoadFromFile");

			// Read the field
			sscanf(pBuffer,"%d",&(Data[i]));

			// Verify the value
			if(Data[i]>(1<<NumBits[i]))
				throw CEvolutiveLibException("Values exceed the representation (number of bits for the field)",__FILE__,__LINE__,"LoadFromFile");


			// Move the pointer
			pBuffer=strchr(pBuffer,' ');

			// Remove the space
			if(pBuffer)
				pBuffer++;
				
		}
		
		// Set the parameters
		m_Population[NumInd].SetFields(NumPars,NumBits);
		m_Population[NumInd].SetCode(CodMethod);

		// Set the data
		m_Population[NumInd].SetDecValue(Data);

		// Increment the individuals counter
		NumInd++;
	}

	// Release the data
	delete[] Data;

	// Close the file
	ifs.close();
}

int Evolutive::CPopulation::GetNumFilesFromFile(string FileName)
{
	fstream ifs;	
	int NumLines;
	char Buffer[1024];
		
	// Open the input file
	ifs.open(FileName.data(),ios_base::in);
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"GetNumFilesFromFile");

	// Count the number of lines
	NumLines=0;
	while(!ifs.eof())
	{
		// Read the line
		ifs.getline(Buffer,1024);		

		// If we have an empty line continue
		if(!ifs.gcount())
			continue;		

		// Increment the counter
		NumLines++;
	}

	// Close the file
	ifs.close();

	return NumLines;
}

bool Evolutive::CPopulation::IsScored(void)
{
	if(!m_PopScores)
		return false;
	if(m_PopScores->size()<m_PopSize)
		return false;

	return true;
}

void Evolutive::CPopulation::Export(string FileName)
{
	fstream ofs;
	int NumFields;
	CODING_METHOD CodingMethod;
	unsigned int *pIntData=NULL;
	double *pDblData=NULL;
	register int i,j;
	
	// Create the output file
	ofs.open(FileName.data(),ios_base::out | ios_base::trunc);
	if(ofs.fail())
		throw CEvolutiveLibException("Cannot open output file",__FILE__,__LINE__,"Export");

	// If the population is empty close the file and return
	if(m_PopSize<=0)
	{
		ofs.close();
		return;
	}
	
	// Obtain the number of fields and the coding method
	NumFields=m_Population[0].GetNumFields();
	CodingMethod=m_Population[0].GetCodingMethod();

	// Write the each individual to the file
	switch(CodingMethod)
	{
	case CODE_DECIMAL_INT:
		for(i=0;i<m_PopSize;i++)
		{
			pIntData=m_Population[i].GetIntStringPtr();
			for(j=0;j<NumFields;j++)
			{		
				ofs << pIntData[j];
				if(j<NumFields-1)
					ofs << " ";
			}
			ofs << endl;
		}
		break;
	case CODE_DECIMAL_DBL:
		for(i=0;i<m_PopSize;i++)
		{
			pDblData=m_Population[i].GetDblStringPtr();
			for(j=0;j<NumFields;j++)
			{		
				ofs << pDblData[j];
				if(j<NumFields-1)
					ofs << " ";
			}
			ofs << endl;
		}
		break;
	}

	// Close the file
	ofs.close();
}

void Evolutive::CPopulation::SetCodMethod(CODING_METHOD CodMethod)
{
	register unsigned int i;

	// Change the codification method to all the individuals
	for(i=0;i<m_PopSize;i++)
	{
		m_Population[i].SetCode(CodMethod);
	}
}
